package com.hang.handler;

import com.alibaba.fastjson.JSON;
import com.hang.entity.Message;
import com.hang.entity.User;
import com.hang.mapstruct.MessageMapStruct;
import com.hang.model.vo.MessageVO;
import com.hang.model.vo.NoticeInfoVO;
import com.hang.service.IMessageService;
import com.hang.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 接口路径 ws://localhost:8080/websocket/userId
 */
@Component
@ServerEndpoint("/websocket/{userId}")
@Slf4j
public class Websocket {
    //客户端连接会话
    private Session session;


    private static Websocket webSocket;

    @Resource
    private IMessageService messageService;

    @Resource
    private MessageMapStruct messageMapStruct;

    @Resource
    private IUserService userService;

    @PostConstruct
    public void init() {
        webSocket = this;
        webSocket.messageService = this.messageService;
        webSocket.messageMapStruct=this.messageMapStruct;
        webSocket.userService=this.userService;
    }


    // 用来存在线连接用户信息
    private static ConcurrentHashMap<Long, Session> sessionPool = new ConcurrentHashMap<>();

    //连接开始调用方法
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") Long userId) {
        this.session = session;
        sessionPool.put(userId, session);

        log.info("【消息】有新的用户连接,连接的用户id为:{}", userId);

        //监测用户是否有未读消息，是则进行消息推送
        noticeMessage(userId);
    }

    //连接关闭调用方法
    @OnClose
    public void onClose(@PathParam(value = "userId") Long userId) {
        //释放会话资源
        sessionPool.remove(userId);
        log.info("【消息】有用户断开了连接,断开连接的用户id为:{}", userId);
    }

    //收到客户端消息后调用的方法
    @OnMessage
    public void onMessage(String message) throws Exception {
        //心跳监测
        if("ping".equals(message)){
            session.getAsyncRemote().sendText("pong");
        }
        log.info("接收到客户端返回的消息:{}", message);
    }

    //发生错误时的处理
    @OnError
    public void onError(Throwable error) {
        log.error("错误,原因:" + error.getMessage());
        log.error(error.toString());
    }

    //实现服务器的主动推送
    public void sendMessage(Message message) throws IOException {
        //对指定消息放推送信息即可
        Session session = sessionPool.get(message.getReceiveId());
        if (session != null && session.isOpen()) {
            MessageVO messageVO = webSocket.messageMapStruct.messageToMessageVO(message);

            String receiveName = Optional.ofNullable(webSocket.userService.lambdaQuery().eq(User::getUserId, message.getReceiveId()).one()).flatMap(user -> Optional.ofNullable(user.getRealName())).orElse("");

            messageVO.setReceiveName(receiveName);

            String messageStr = JSON.toJSONString(messageVO);

            //将内容转换发送
            session.getAsyncRemote().sendText(messageStr);
        }
    }

    //查看用户是否有未读消息，如果有则进行提醒
    public void noticeMessage(Long userId) {
        //查找消息表
        Integer messageCount = Optional
                .ofNullable(
                        webSocket.messageService.lambdaQuery()
                                .eq(Message::getReceiveId, userId)
                                .eq(Message::getIsRead, 0)
                                .count()
                )
                .orElse(0);

        if (messageCount > 0) {
            //如果有消息，那么将消息封装并实现推送(推送所有消息)
            List<Message> messageList = webSocket.messageService.lambdaQuery().eq(Message::getReceiveId, userId).list();
            messageList.forEach(
                    message -> {
                        try {
                            sendMessage(message);
                        } catch (IOException e) {
                            log.error(e.toString());
                        }
                    }
            );
        }
    }

    //项目发布公告,将公告消息推送给所有这个项目的成员
    public void sendNoticeToItemUserList(List<Long> userIdList, NoticeInfoVO noticeInfoVO) throws IOException {
        userIdList.forEach(
                userId -> {
                    Session session = sessionPool.get(userId);
                    if (session != null && session.isOpen()) {
                        //转成公告消息，然后群发
                        String noticeStr = JSON.toJSONString(noticeInfoVO);

                        //发送
                        session.getAsyncRemote().sendText(noticeStr);
                    }
                }
        );
    }
}
